package com.rolerealm.service.impl;

import com.rolerealm.common.constant.ErrorCode;
import com.rolerealm.common.exception.BusinessException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.rolerealm.dao.ConversationMapper;
import com.rolerealm.dao.ConversationMessageMapper;
import com.rolerealm.domain.dto.ConversationDTOs.MessageRequest;
import com.rolerealm.domain.dto.ConversationDTOs.MessageResponse;
import com.rolerealm.domain.dto.ConversationDTOs.ConversationCreateRequest;
import com.rolerealm.domain.entity.ConversationEntity;
import com.rolerealm.domain.entity.ConversationMessageEntity;
import com.rolerealm.domain.vo.CharacterDetailVO;
import com.rolerealm.domain.vo.ConversationListItemVO;
import com.rolerealm.domain.vo.ConversationMessageVO;
import com.rolerealm.domain.vo.PageResult;
import com.rolerealm.service.CharacterService;
import com.rolerealm.service.ConversationService;
import com.rolerealm.domain.enums.SkillType;
import com.rolerealm.domain.enums.SenderType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.time.LocalDateTime;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
@RequiredArgsConstructor
public class ConversationServiceImpl implements ConversationService {

    private final ConversationMapper conversationMapper;
    private final ConversationMessageMapper conversationMessageMapper;
    private final CharacterService characterService;
    private final StringRedisTemplate stringRedisTemplate;
    private final com.rolerealm.service.SkillService skillService;
    private static final int MAX_PAGE_SIZE = 100;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createConversation(Long userId, ConversationCreateRequest request) {
        CharacterDetailVO character = characterService.getCharacterById(request.getCharacterId());
        if (character == null) {
            throw new BusinessException(ErrorCode.CHARACTER_NOT_FOUND);
        }

        ConversationEntity entity = ConversationEntity.builder()
                .userId(userId)
                .characterId(request.getCharacterId())
                .title(request.getTitle())
                .skillType(request.getSkillType() != null ? request.getSkillType() : "qa")
                .messageCount(0)
                .status(1)
                .createdAt(LocalDateTime.now())
                .updatedAt(LocalDateTime.now())
                .build();

        int inserted = conversationMapper.insert(entity);
        if (inserted != 1 || entity.getId() == null) {
            throw new BusinessException(ErrorCode.CONVERSATION_CREATE_FAILED);
        }
        // 初始化会话上下文缓存（异步执行，避免影响事务）
        try {
            writeConversationContext(entity.getId(), userId, entity.getCharacterId(), entity.getSkillType());
        } catch (Exception e) {
            log.warn("初始化对话上下文缓存失败，但不影响对话创建，对话ID：{}，错误：{}", entity.getId(), e.getMessage());
        }
        return entity.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public MessageResponse sendMessage(Long conversationId, Long userId, MessageRequest request) {
        ConversationEntity conversation = conversationMapper.selectById(conversationId);
        if (conversation == null || conversation.getDeleted() != null && conversation.getDeleted() == 1) {
            throw new BusinessException(ErrorCode.CONVERSATION_NOT_FOUND);
        }
        if (!conversation.getUserId().equals(userId)) {
            throw new BusinessException(ErrorCode.UNAUTHORIZED);
        }

        // 保存用户消息
        ConversationMessageEntity userMsg = ConversationMessageEntity.builder()
                .conversationId(conversationId)
                .senderType(SenderType.USER.getCode())
                .content(request.getContent())
                .createdAt(LocalDateTime.now())
                .build();
        conversationMessageMapper.insert(userMsg);

        // 调用技能服务最小可用流水线（策略→路由→LLM）
        var execReq = com.rolerealm.domain.dto.SkillDTOs.SkillExecuteRequest.builder()
                .characterId(conversation.getCharacterId())
                .conversationId(conversationId)
                .input(request.getContent())
                .build();
        String aiText = skillService.execute(conversation.getSkillType(), execReq).getContent();

        ConversationMessageEntity aiMsg = ConversationMessageEntity.builder()
                .conversationId(conversationId)
                .senderType(SenderType.AI.getCode())
                .content(aiText)
                .createdAt(LocalDateTime.now())
                .build();
        conversationMessageMapper.insert(aiMsg);

        // 更新会话计数与最后时间（带重试机制处理死锁）
        conversation.setMessageCount(conversation.getMessageCount() == null ? 2 : conversation.getMessageCount() + 2);
        conversation.setLastMessageAt(LocalDateTime.now());
        conversation.setUpdatedAt(LocalDateTime.now());
        
        // 重试机制处理数据库死锁
        int retryCount = 0;
        int maxRetries = 3;
        while (retryCount < maxRetries) {
            try {
                conversationMapper.updateById(conversation);
                break; // 成功则跳出循环
            } catch (org.springframework.dao.DataAccessException e) {
                // 检查是否是死锁异常
                if (!e.getMessage().contains("Deadlock found")) {
                    throw e; // 不是死锁异常，直接抛出
                }
                retryCount++;
                if (retryCount >= maxRetries) {
                    log.error("更新对话信息失败，已达到最大重试次数，对话ID：{}", conversationId);
                    throw e; // 重试次数用完，抛出异常
                }
                log.warn("数据库死锁，正在重试第{}次，对话ID：{}", retryCount, conversationId);
                try {
                    Thread.sleep(50 + retryCount * 50); // 递增延迟：50ms, 100ms, 150ms
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new BusinessException(ErrorCode.CONVERSATION_UPDATE_FAILED);
                }
            }
        }

        // 刷新会话上下文缓存（保留最近若干条）
        writeConversationContext(conversationId, userId, conversation.getCharacterId(), conversation.getSkillType());

        return MessageResponse.builder()
                .messageId(aiMsg.getId())
                .content(aiText)
                .build();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void switchSkill(Long conversationId, Long userId, String skillType) {
        if (!SkillType.isValid(skillType)) {
            throw new BusinessException(ErrorCode.SKILL_NOT_FOUND);
        }

        ConversationEntity conversation = conversationMapper.selectById(conversationId);
        if (conversation == null || conversation.getDeleted() != null && conversation.getDeleted() == 1) {
            throw new BusinessException(ErrorCode.CONVERSATION_NOT_FOUND);
        }
        if (!conversation.getUserId().equals(userId)) {
            throw new BusinessException(ErrorCode.UNAUTHORIZED);
        }

        conversation.setSkillType(skillType.toLowerCase());
        conversation.setUpdatedAt(LocalDateTime.now());
        
        // 重试机制处理数据库死锁
        int retryCount = 0;
        int maxRetries = 3;
        int updated = 0;
        while (retryCount < maxRetries) {
            try {
                updated = conversationMapper.updateById(conversation);
                break; // 成功则跳出循环
            } catch (org.springframework.dao.DataAccessException e) {
                // 检查是否是死锁异常
                if (!e.getMessage().contains("Deadlock found")) {
                    throw e; // 不是死锁异常，直接抛出
                }
                retryCount++;
                if (retryCount >= maxRetries) {
                    log.error("更新对话技能类型失败，已达到最大重试次数，对话ID：{}", conversationId);
                    throw e; // 重试次数用完，抛出异常
                }
                log.warn("数据库死锁，正在重试第{}次，对话ID：{}", retryCount, conversationId);
                try {
                    Thread.sleep(50 + retryCount * 50); // 递增延迟：50ms, 100ms, 150ms
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new BusinessException(ErrorCode.CONVERSATION_UPDATE_FAILED);
                }
            }
        }
        
        if (updated != 1) {
            throw new BusinessException(ErrorCode.CONVERSATION_UPDATE_FAILED);
        }

        // 刷新上下文缓存中的 skillType
        writeConversationContext(conversationId, userId, conversation.getCharacterId(), conversation.getSkillType());
    }

    @Override
    public PageResult<ConversationListItemVO> pageUserConversations(Long userId, Long characterId, int page, int size) {
        Assert.isTrue(page >= 1, "page must be >= 1");
        Assert.isTrue(size >= 1 && size <= MAX_PAGE_SIZE, "size out of range");

        Page<ConversationEntity> mpPage = new Page<>(page, size);
        LambdaQueryWrapper<ConversationEntity> wrapper = new LambdaQueryWrapper<ConversationEntity>()
                .eq(ConversationEntity::getUserId, userId)
                .eq(ConversationEntity::getDeleted, 0)
                // 只显示有消息的对话（message_count > 0）
                .gt(ConversationEntity::getMessageCount, 0)
                .orderByDesc(ConversationEntity::getLastMessageAt)
                .orderByDesc(ConversationEntity::getCreatedAt);
        
        // 如果指定了角色ID，则只查询该角色的对话历史
        if (characterId != null) {
            wrapper.eq(ConversationEntity::getCharacterId, characterId);
        }
        
        IPage<ConversationEntity> records = conversationMapper.selectPage(mpPage, wrapper);

        var list = records.getRecords().stream().map(c -> {
            ConversationListItemVO vo = new ConversationListItemVO();
            vo.setId(c.getId());
            vo.setTitle(c.getTitle());
            vo.setCharacterId(c.getCharacterId());
            vo.setSkillType(c.getSkillType());
            vo.setMessageCount(c.getMessageCount());
            vo.setLastMessageAt(c.getLastMessageAt());
            vo.setCreatedAt(c.getCreatedAt());
            return vo;
        }).toList();

        return PageResult.of(list, page, size, records.getTotal());
    }

    @Override
    public PageResult<ConversationMessageVO> pageMessages(Long conversationId, Long userId, int page, int size) {
        Assert.isTrue(page >= 1, "page must be >= 1");
        Assert.isTrue(size >= 1 && size <= MAX_PAGE_SIZE, "size out of range");

        ConversationEntity conv = conversationMapper.selectById(conversationId);
        if (conv == null || (conv.getDeleted() != null && conv.getDeleted() == 1) || !conv.getUserId().equals(userId)) {
            throw new BusinessException(ErrorCode.CONVERSATION_NOT_FOUND);
        }

        Page<ConversationMessageEntity> mpPage = new Page<>(page, size);
        LambdaQueryWrapper<ConversationMessageEntity> wrapper = new LambdaQueryWrapper<ConversationMessageEntity>()
                .eq(ConversationMessageEntity::getConversationId, conversationId)
                .eq(ConversationMessageEntity::getDeleted, 0)
                .orderByAsc(ConversationMessageEntity::getCreatedAt);
        IPage<ConversationMessageEntity> records = conversationMessageMapper.selectPage(mpPage, wrapper);

        var list = records.getRecords().stream().map(m -> {
            ConversationMessageVO vo = new ConversationMessageVO();
            vo.setId(m.getId());
            vo.setSenderType(SenderType.fromCode(m.getSenderType()).getValue());
            vo.setContent(m.getContent());
            vo.setAudioUrl(m.getAudioUrl());
            vo.setAudioDuration(m.getAudioDuration());
            vo.setCreatedAt(m.getCreatedAt());
            return vo;
        }).toList();

        return PageResult.of(list, page, size, records.getTotal());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteConversation(Long conversationId, Long userId) {
        ConversationEntity conv = conversationMapper.selectById(conversationId);
        if (conv == null || (conv.getDeleted() != null && conv.getDeleted() == 1) || !conv.getUserId().equals(userId)) {
            throw new BusinessException(ErrorCode.CONVERSATION_NOT_FOUND);
        }

        // 逻辑删除会话（带重试机制处理死锁）
        conv.setDeleted(1);
        conv.setUpdatedAt(LocalDateTime.now());
        
        // 重试机制处理数据库死锁
        int retryCount = 0;
        int maxRetries = 3;
        int updated = 0;
        while (retryCount < maxRetries) {
            try {
                updated = conversationMapper.updateById(conv);
                break; // 成功则跳出循环
            } catch (org.springframework.dao.DataAccessException e) {
                // 检查是否是死锁异常
                if (!e.getMessage().contains("Deadlock found")) {
                    throw e; // 不是死锁异常，直接抛出
                }
                retryCount++;
                if (retryCount >= maxRetries) {
                    log.error("删除对话失败，已达到最大重试次数，对话ID：{}", conversationId);
                    throw e; // 重试次数用完，抛出异常
                }
                log.warn("数据库死锁，正在重试第{}次，对话ID：{}", retryCount, conversationId);
                try {
                    Thread.sleep(50 + retryCount * 50); // 递增延迟：50ms, 100ms, 150ms
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new BusinessException(ErrorCode.CONVERSATION_UPDATE_FAILED);
                }
            }
        }
        
        if (updated != 1) {
            throw new BusinessException(ErrorCode.CONVERSATION_UPDATE_FAILED);
        }

        // 逻辑删除消息（用条件更新）
        LambdaQueryWrapper<ConversationMessageEntity> msgWrapper = new LambdaQueryWrapper<ConversationMessageEntity>()
                .eq(ConversationMessageEntity::getConversationId, conversationId)
                .eq(ConversationMessageEntity::getDeleted, 0);
        // 逻辑删除（MyBatis-Plus @TableLogic 自动处理）
        conversationMessageMapper.delete(msgWrapper);

        // 删除会话上下文缓存
        try {
            if (stringRedisTemplate != null) {
                stringRedisTemplate.delete("conversation:context:" + conversationId);
                log.debug("已删除对话上下文缓存，对话ID：{}", conversationId);
            }
        } catch (Exception e) {
            log.warn("Redis缓存不可用，跳过删除对话上下文缓存，对话ID：{}，错误：{}", conversationId, e.getMessage());
        }
    }

    /**
     * 写入或刷新会话上下文缓存：conversation:context:{conversationId}
     * 结构：{"conversationId":..,"userId":..,"characterId":..,"skillType":"..","recentMessages":[...]}
     */
    private void writeConversationContext(Long conversationId, Long userId, Long characterId, String skillType) {
        if (stringRedisTemplate == null) {
            log.debug("Redis模板不可用，跳过对话上下文缓存");
            return;
        }

        // 查询最近的 10 条消息（按时间升序存放）
        Page<ConversationMessageEntity> mpPage = new Page<>(1, 10);
        LambdaQueryWrapper<ConversationMessageEntity> wrapper = new LambdaQueryWrapper<ConversationMessageEntity>()
                .eq(ConversationMessageEntity::getConversationId, conversationId)
                .eq(ConversationMessageEntity::getDeleted, 0)
                .orderByDesc(ConversationMessageEntity::getCreatedAt);
        IPage<ConversationMessageEntity> page = conversationMessageMapper.selectPage(mpPage, wrapper);
        List<ConversationMessageEntity> recentDesc = page.getRecords();
        recentDesc.sort(Comparator.comparing(ConversationMessageEntity::getCreatedAt));

        List<Map<String, Object>> recentMessages = new ArrayList<>();
        for (ConversationMessageEntity m : recentDesc) {
            Map<String, Object> item = new HashMap<>();
            item.put("senderType", SenderType.fromCode(m.getSenderType()).getValue());
            item.put("content", m.getContent());
            item.put("createdAt", m.getCreatedAt() != null ? m.getCreatedAt().toString() : null);
            recentMessages.add(item);
        }

        try {
            Map<String, Object> context = new HashMap<>();
            context.put("conversationId", conversationId);
            context.put("userId", userId);
            context.put("characterId", characterId);
            context.put("skillType", skillType);
            context.put("recentMessages", recentMessages);

            String key = "conversation:context:" + conversationId;
            String json = toJson(context);
            stringRedisTemplate.opsForValue().set(key, json, Duration.ofHours(2));
            log.debug("对话上下文已写入Redis缓存，对话ID：{}", conversationId);
        } catch (Exception e) {
            log.warn("Redis缓存不可用，跳过对话上下文缓存，对话ID：{}，错误：{}", conversationId, e.getMessage());
            // Redis不可用时不影响主要功能，只记录警告日志
        }
    }

    private String toJson(Map<String, Object> map) {
        // 轻量 JSON 序列化，避免引入新依赖
        StringBuilder sb = new StringBuilder();
        sb.append('{');
        boolean first = true;
        for (Map.Entry<String, Object> e : map.entrySet()) {
            if (!first) sb.append(',');
            first = false;
            sb.append('"').append(escape(e.getKey())).append('"').append(':');
            sb.append(valueToJson(e.getValue()));
        }
        sb.append('}');
        return sb.toString();
    }

    private String valueToJson(Object v) {
        if (v == null) return "null";
        if (v instanceof Number || v instanceof Boolean) return String.valueOf(v);
        if (v instanceof String s) return '"' + escape(s) + '"';
        if (v instanceof List<?> list) {
            StringBuilder sb = new StringBuilder("[");
            boolean first = true;
            for (Object o : list) {
                if (!first) sb.append(',');
                first = false;
                sb.append(valueToJson(o));
            }
            sb.append(']');
            return sb.toString();
        }
        if (v instanceof Map<?, ?> m) {
            StringBuilder sb = new StringBuilder("{");
            boolean first = true;
            for (Map.Entry<?, ?> e : m.entrySet()) {
                if (!first) sb.append(',');
                first = false;
                sb.append('"').append(escape(String.valueOf(e.getKey()))).append('"').append(':');
                sb.append(valueToJson(e.getValue()));
            }
            sb.append('}');
            return sb.toString();
        }
        return '"' + escape(String.valueOf(v)) + '"';
    }

    private String escape(String s) {
        return s.replace("\\", "\\\\").replace("\"", "\\\"");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int cleanEmptyConversations(Long userId) {
        // 查找该用户的所有空对话（message_count = 0 或 null）
        LambdaQueryWrapper<ConversationEntity> wrapper = new LambdaQueryWrapper<ConversationEntity>()
                .eq(ConversationEntity::getUserId, userId)
                .eq(ConversationEntity::getDeleted, 0)
                .and(w -> w.eq(ConversationEntity::getMessageCount, 0).or().isNull(ConversationEntity::getMessageCount));
        
        List<ConversationEntity> emptyConversations = conversationMapper.selectList(wrapper);
        
        if (emptyConversations.isEmpty()) {
            log.info("用户 {} 没有空对话需要清理", userId);
            return 0;
        }
        
        int cleanedCount = 0;
        for (ConversationEntity conversation : emptyConversations) {
            try {
                // 逻辑删除空对话
                conversation.setDeleted(1);
                conversation.setUpdatedAt(LocalDateTime.now());
                conversationMapper.updateById(conversation);
                
                // 删除相关的消息记录
                LambdaQueryWrapper<ConversationMessageEntity> msgWrapper = new LambdaQueryWrapper<ConversationMessageEntity>()
                        .eq(ConversationMessageEntity::getConversationId, conversation.getId())
                        .eq(ConversationMessageEntity::getDeleted, 0);
                conversationMessageMapper.delete(msgWrapper);
                
                // 删除Redis缓存
                try {
                    if (stringRedisTemplate != null) {
                        stringRedisTemplate.delete("conversation:context:" + conversation.getId());
                    }
                } catch (Exception e) {
                    log.warn("删除Redis缓存失败，对话ID：{}，错误：{}", conversation.getId(), e.getMessage());
                }
                
                cleanedCount++;
                log.info("已清理空对话，对话ID：{}，用户ID：{}", conversation.getId(), userId);
                
            } catch (Exception e) {
                log.error("清理空对话失败，对话ID：{}，用户ID：{}，错误：{}", conversation.getId(), userId, e.getMessage());
            }
        }
        
        log.info("用户 {} 的空对话清理完成，共清理 {} 个空对话", userId, cleanedCount);
        return cleanedCount;
    }
}


